package models.base;

import ext.Func;
import ext.func.FuncDefine;
import ext.jpa.UUIDModelWithDiff;
import helper.DateTimeFormater;
import helper.GlobalConfig;
import helper.UrlHelper;
import models.constants.DeletedStatus;
import models.enums.LangCode;
import models.enums.LicenseEdition;
import models.enums.OpenIdSource;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.builder.DiffBuilder;
import org.apache.commons.lang3.builder.DiffResult;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hibernate.annotations.Index;
import play.Logger;
import play.modules.paginate.JPAExtPaginator;
import util.DateHelper;
import util.common.CollectionHelper;
import util.common.DateUtil;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 公司信息.
 *
 * 企业号应用可以有多个公司运行，需要后台可以开通公司，开通后，会生成一个对接api url.
 *
 * 也可以通过微信公众号管理公司.
 *
 */
@Entity
@Table(name = "corps", schema = GlobalConfig.MIGAO_DB_SCHEMA)
@org.hibernate.annotations.Table(
        appliesTo = "corps",
        indexes = {
                @Index(name = "idx_corps_createdat", columnNames = "created_at")
//                ,@Index(name = "idx_corps_root_corp", columnNames = "root_corp_id")
        }
)
public class Corp extends UUIDModelWithDiff<Corp> {

    private static final long serialVersionUID = 2612836035231152552L;

    /**
     * 公司名.
     */
    @Column(name = "name", length = 50)
    public String name;

    /**
     * 冻结
     */
    @Column(name = "is_enabled")
    public Boolean enabled;

    /**
     * 失效时间.  付费有效期使用这个字段.
     *
     * 如果有值，则在此值之前此公司是可用的.
     * 如果无值，则依赖于enabled值.
     */
    @Column(name = "expired_at")
    public Date expiredAt;

    /**
     * 创建时间.
     */
    @Column(name = "created_at")
    public Date createdAt;

    /**
     * 更新时间.
     */
    @Column(name = "updated_at")
    public Date updatedAt;

    /**
     * 公司邮箱后缀，@部分的内容，全小写；多个时用小写逗号分隔
     *
     * 如： safeguard.com
     */
    @Column(name = "email_postfix", length = 50)
    public String emailPostfix;


    /**
     * 公司logo的Ali OSS Object名字.
     * 方形logo.
     */
    @Column(name = "logo_object", length = 250)
    public String logoObject;

    /**
     * Pad设备上使用的Logo，长方形.
     */
    @Column(name = "pad_logo_object", length = 250)
    public String padLogoObject;

    /**
     * 注册来源.
     */
    @Enumerated(EnumType.STRING)
    @Column(name = "register_source", length = 20)
    public OpenIdSource registerSource;

    /**
     * 公司绑定的平台，如 微信服务号、钉钉等； 默认微信服务号.
     */
    @Enumerated(EnumType.STRING)
    @Column(name = "bind_platform", length = 10)
    public OpenIdSource bindPlatform;

    /**
     * 逻辑删除,0:未删除，1:已删除
     */
    @Enumerated(EnumType.ORDINAL)
    @Column(name = "deleted")
    public DeletedStatus deleted = DeletedStatus.UN_DELETED;

    /**
     * 公司通告.
     */
    @Column(name = "notice", length = 250)
    public String notice;

    /**
     * 公司级别的语言.
     */
    @Enumerated(EnumType.STRING)
    @Column(name = "lang", length = 8)
    public LangCode lang;

    /**
     * 公司授权类型.
     */
    @Enumerated(EnumType.STRING)
    @Column(name = "license_edition", length = 20)
    public LicenseEdition licenseEdition;

    /**
     * 增加的功能.
     */
    @Column(name = "addition_funcs", length = 1024)
    public String additionFuncs;

    /**
     * 公司禁用的功能
     */
    @Column(name = "disabled_funcs", length = 250)
    public String disabledFuncs;

    /**
     * 不使用的角色.
     */
    @Column(name = "disabled_roles", length = 200)
    public String disabledRoles;

    /**
     * 上游系统的公司ID. 如果是LDAP, 使用distinguishedName
     */
    @Column(name = "upstream_id", length = 250)
    public String upstreamId;

    /**
     * 集团总公司ID.
     * 如果为空, 表示不需要处理这方面的逻辑.
     */
    @Column(name = "root_corp_id", columnDefinition = "char(22)")
    public String rootCorpId;

    /**
     * 平板视图URL.
     */
    @Column(name = "tablet_view_url", length = 250)
    public String tabletViewUrl;

    /**
     * 自定义Web资源，可包括JS/CSS，会拼接到 GlobalConfig.WEB_ASSET_BASE 后面(以http开头的不会).
     * 如果有值，将覆盖 config: web.custom.assets 的值
     */
    @Column(name = "custom_web_assets", length = 1000)
    public String customWebAssets;

    /**
     * 自定义Web资源，可包括JS/CSS，会拼接到 GlobalConfig.MOBILE_ASSET_BASE 后面(以http开头的不会).
     * 如果有值，将覆盖 config: mobile.custom.assets 的值
     *
     * FIXME: 因为前端已经合并了 web和mobile，此字段不再需要.
     */
    @Column(name = "custom_mobile_assets", length = 1000)
    public String customMobileAssets;

    /**
     * 查找有效的Corp. 不检查expiredAt.
     */
    public static Corp findAvailableByName(String name) {
        return Corp.find("name=?1 and enabled=?2 and deleted=?3",
                name, Boolean.TRUE, DeletedStatus.UN_DELETED).first();
    }

    /**
     * 按名字找，只用于私有安装版.
     */
    public static Corp findByName(String name) {
        return Corp.find("name=?1", name).first();
    }

    public static Corp findAvailableById(String id) {
        return Corp.find("id=?1 and enabled=?2 and deleted=?3",
                id, Boolean.TRUE, DeletedStatus.UN_DELETED).first();
    }

    /**
     * 查找有效的Corp. 不检查expiredAt.
     */
    public static List<Corp> findLikeCorps(String name) {
        return Corp.find("name=?1 and enabled=?2 and deleted=?3",
                name, Boolean.TRUE, DeletedStatus.UN_DELETED).fetch(5);
    }

    /**
     * 查找有效的Corp.
     */
    public static List<Corp> findAvailables() {
        return Corp.find("deleted=?1 and enabled=?2",
                DeletedStatus.UN_DELETED, Boolean.TRUE).fetch();
    }

    public static List<Corp> findUndeleted() {
        return Corp.find("deleted=?1 order by createdAt desc",
                DeletedStatus.UN_DELETED).fetch();
    }

    public static Corp createDefaultTrialCorp(String name, int validDays) {
        Corp corp = new Corp();
        corp.name = name;
        corp.enabled = Boolean.TRUE;
        corp.expiredAt = DateUtil.getEndOfDay(DateHelper.afterDays(validDays));
        corp.createdAt = new Date();
        corp.licenseEdition = LicenseEdition.TRIAL;  // 默认是试用版
        corp.updatedAt = new Date();
        corp.deleted = models.constants.DeletedStatus.UN_DELETED;
        return corp;
    }

    @Transient
    public boolean isAvaliable() {
        return !isExpired(); // 没过期
    }

    @Transient
    public String getDefaultUrl() {
        return UrlHelper.getWebsiteBaseUrl();
    }

    @Transient
    public String getSafeLogoObject() {
        if (StringUtils.isNotBlank(this.logoObject)) {
            return this.logoObject;
        }
        return GlobalConfig.CORP_DEFAULT_LOGO_URL;
    }

    /**
     * licenseEdition对应的Funcs
     */
    @Transient
    public Set<Func> getLicensedFuncSet() {
        if (this.licenseEdition != null) {
            return FuncDefine.getFuncSet(this.licenseEdition);
        }
        Logger.info("LOG01830: 没有授予License,用TRIAL");
        return FuncDefine.getFuncSet(LicenseEdition.TRIAL);
    }

    @Transient
    private Set<Func> _disabledFuncSet;

    /**
     * 设置禁用的Funcs
     */
    @Transient
    public Set<Func> getDisabledFuncSet() {
        if (_disabledFuncSet == null) {
            _disabledFuncSet = new HashSet<>();
            if (StringUtils.isNotBlank(this.disabledFuncs)) {
                String[] disabledFuncNames = this.disabledFuncs.split(",");
                for (String disabledFuncName : disabledFuncNames) {
                    if (StringUtils.isNotBlank(disabledFuncName)) {
                        try {
                            Func func = Func.valueOf(disabledFuncName);
                            _disabledFuncSet.add(func);
                        } catch (IllegalArgumentException e) {
                            Logger.warn("LOG01841: invalid Func Code=" + disabledFuncName);
                        }
                    }
                }
            }
        }
        return _disabledFuncSet;
    }

    @Transient
    private Set<String> _disabledRoleSet;

    /**
     * 设置禁用的Funcs
     */
    @Transient
    public Set<String> getDisabledRoleSet() {
        if (_disabledRoleSet == null) {
            _disabledRoleSet = CollectionHelper.convertStringToSetUseSplit(this.disabledRoles, ",");
        }
        return _disabledRoleSet;
    }

    @Transient
    private Set<Func> _cachedCorpFuncSet;

    public void clearCache() {
        _cachedCorpFuncSet = null;
        _disabledFuncSet = null;
        _disabledRoleSet = null;
    }

    @Transient
    public Set<Func> getCorpFuncSet() {
        if (_cachedCorpFuncSet == null) {
            Logger.debug("LOG01850: ========> _cachedCorpFuncSet NOT Cached!!! load again!!!");
            _cachedCorpFuncSet = new HashSet<>();
            Set<Func> licensedFuncSet = getLicensedFuncSet();
            if (licensedFuncSet != null) {
                _cachedCorpFuncSet.addAll(licensedFuncSet);
            }


            if (StringUtils.isNotBlank(GlobalConfig.DEFAULT_FUNC_LIST)) {
                addAdditionFuncsToCachedCorpFuncSet(GlobalConfig.DEFAULT_FUNC_LIST);
            }

            if (StringUtils.isNotBlank(this.additionFuncs)) {
                addAdditionFuncsToCachedCorpFuncSet(this.additionFuncs);
            }
            // 去掉公司设置为关闭的功能.
            Set<Func> disabledFuncSet = getDisabledFuncSet();
            if (disabledFuncSet.size() > 0) {
                disabledFuncSet.forEach(func -> _cachedCorpFuncSet.remove(func));
            }
        }
        return _cachedCorpFuncSet;
    }

    private void addAdditionFuncsToCachedCorpFuncSet(String additionFuncs) {
        String[] splitFuncNames = additionFuncs.split(",");
        for (String splitFuncName : splitFuncNames) {
            if (StringUtils.isNotBlank(splitFuncName)) {
                try {
                    Func func = Func.valueOf(splitFuncName);
                    _cachedCorpFuncSet.add(func);
                } catch (IllegalArgumentException e) {
                    Logger.warn("LOG01840: invalid Func Code=" + splitFuncName);
                }
            }
        }
    }

    /**
     * 公司绑定的平台，如 微信服务号、钉钉等； 默认微信服务号.
     */
    @Transient
    public OpenIdSource bindPlatform() {
        return this.bindPlatform == null ? OpenIdSource.WXMP : this.bindPlatform;
    }

    /**
     * 是否支持同步组织架构.
     */
    @Transient
    public boolean canSyncOrgan() {
        return this.bindPlatform() == OpenIdSource.DING || this.hasFunc(Func.LDAP);
    }

    /**
     * 检查公司是否有指定功能使用权.
     */
    public boolean hasFunc(Func func) {
        return getCorpFuncSet().contains(func);
    }

    public boolean notNeedEmail() {
        return hasFunc(Func.NO_EMAIL);
    }

    public String platformName() {
        return GlobalConfig.APP_NAME;
    }


    /**
     * 是否集团公司的总公司
     */
    @Transient
    public boolean isRootCorp() {
        return this.rootCorpId == null || this.rootCorpId.equals(this.id);
    }

    /**
     * 安全的读取 rootCorpId.
     * rootCorpId 为空就返回自己的 id
     */
    @Transient
    public String rootCorpId() {
        return this.rootCorpId != null ? this.rootCorpId : this.id;
    }

    /**
     * 读取当前的License类型, 其中试用版过期后自动返回FREE.
     */
    @Transient
    public LicenseEdition getCurrentLicenseEdition() {
        if (isExpired()) {
            // 试用期过期后自动free
            return LicenseEdition.FREE;
        }
        return this.licenseEdition;
    }

    public boolean isExpired() {
        if (this.expiredAt == null) {
            return false;
        }
        return new Date().after(DateUtil.getEndOfDay(this.expiredAt));
    }

    /**
     * 分页查询.
     */
    public static JPAExtPaginator<Corp> findByCondition(Map<String, Object> conditionMap, String orderByExpress, int pageNumber, int pageSize) {

        StringBuilder xsqlBuilder = new StringBuilder(" t.deleted=models.constants.DeletedStatus.UN_DELETED ")
                .append("/~ and t.id = {id} ~/")
                .append("/~ and t.name = {name} ~/")
                .append("/~ and t.name like {corpName} ~/")
                .append("/~ and t.enabled = {enabled} ~/")
                .append("/~ and t.expiredAt = {expiredAt} ~/")
                .append("/~ and t.createdAt = {createdAt} ~/")
                .append("/~ and t.emailPostfix = {emailPostfix} ~/")
                .append("/~ and t.updatedAt = {updatedAt} ~/")
                .append("/~ and t.userMax = {userMax} ~/");

        util.xsql.XsqlBuilder.XsqlFilterResult result = new util.xsql.XsqlBuilder().generateHql(xsqlBuilder.toString(), conditionMap);
        Logger.info("查询语句条件 : " + result.getXsql() + "====");
        JPAExtPaginator<Corp> corpPage = new JPAExtPaginator<Corp>("Corp t", "t", Corp.class,
                result.getXsql(), conditionMap).orderBy(orderByExpress);
        corpPage.setPageNumber(pageNumber);
        corpPage.setPageSize(pageSize);
        corpPage.setBoundaryControlsEnabled(false);
        return corpPage;
    }

    @Override public String toString() {
        final StringBuilder sb = new StringBuilder("Corp{");
        sb.append("id='").append(id).append('\'');
        sb.append(", name='").append(name).append('\'');
        sb.append(", enabled=").append(enabled);
        sb.append(", expiredAt=").append(expiredAt);
        sb.append(", logoObject=").append(logoObject);
        sb.append(", emailPostfix=").append(emailPostfix);
        sb.append(", deleted=").append(deleted);
        sb.append(", isAvaliable=").append(isAvaliable());
        sb.append('}');
        return sb.toString();
    }

    /**
     * 设置需要关注的diff.
     */
    @Override
    public DiffResult diff(Corp obj) {
        return new DiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
                .append("name", this.name, obj.name)
                .append("enabled", this.enabled, obj.enabled)
                .append("expiredAt", DateTimeFormater.date(this.expiredAt), DateTimeFormater.date(obj.expiredAt))
                .append("emailPostfix", this.emailPostfix, obj.emailPostfix)
                .append("logoObject", this.logoObject, obj.logoObject)
                .append("notice", this.notice, obj.notice)
                .append("licenseEdition", this.licenseEdition, obj.licenseEdition)
                .append("additionFuncs", this.additionFuncs, obj.additionFuncs)
                .append("disabledFuncs", this.disabledFuncs, obj.disabledFuncs)
                .append("disabledRoles", this.disabledRoles, obj.disabledRoles)
                .append("customWebAssets", this.customWebAssets, obj.customWebAssets)
                .append("customMobileAssets", this.customMobileAssets, obj.customMobileAssets)
                .append("tabletViewUrl", this.tabletViewUrl, obj.tabletViewUrl)
                .build();
    }

    public static Corp findFirst() {
        return Corp.find("deleted=?1 and enabled=?2 and expiredAt >= ?3",
                DeletedStatus.UN_DELETED, Boolean.TRUE, new Date()).first();
    }

    /**
     * 获取公司默认语言.
     */
    public String currentLang() {
        return currentLang(LangCode.zh);
    }
    public String currentLang(LangCode defaultLang) {
        if (this.lang == null) {
            return defaultLang == null ? null : defaultLang.toString();
        }
        return this.lang.toString();
    }

    /**
     * 按集团公司查找 。
     */
    public static List<Corp> findAllByRootCorp(Corp corp) {
        return Corp.find("(id=?1 or rootCorpId=?2) and deleted=?3 order by createdAt",
                corp.rootCorpId(), corp.rootCorpId(), DeletedStatus.UN_DELETED).fetch();
    }


    public static Corp findChildCorpById(Corp myCorp, String id) {
        return Corp.find("rootCorpId=?1 and id=?2 and deleted=?3 order by createdAt",
                myCorp.rootCorpId(), id, DeletedStatus.UN_DELETED).first();
    }
}
